package vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.renderers;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observer;
import java.util.Set;

import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

import org.apache.commons.lang.mutable.MutableBoolean;

import com.mxgraph.analysis.mxGraphAnalysis;
import com.mxgraph.analysis.mxICostFunction;
import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.mxGraphOutline;
import com.mxgraph.swing.handler.mxKeyboardHandler;
import com.mxgraph.swing.handler.mxRubberband;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxUndoManager;
import com.mxgraph.util.mxUndoableEdit;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.util.mxUndoableEdit.mxUndoableChange;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxStylesheet;

import vg.services.data_base_manager.data.graph.Attribute;
import vg.services.data_base_manager.data.graph.AttributeItem;
import vg.services.data_base_manager.data.graph.Edge;
import vg.services.data_base_manager.data.graph.Graph;
import vg.services.data_base_manager.data.graph.Vertex;
import vg.services.graph_view_manager.interfaces.graph_view.AGraphView;
import vg.services.graph_view_manager.interfaces.graph_view.IGraphSettings;
import vg.services.graph_view_manager.realization.graph_view.shared.graph_lib_depending.AJGraphComponents;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.SmartGraphView;
import vg.services.main_manager.MainManager;
import vg.services.plugin_manager.event_and_request.event.graph_editor.AddAttributeEvent;
import vg.services.plugin_manager.event_and_request.event.graph_editor.AddEdgeEvent;
import vg.services.plugin_manager.event_and_request.event.graph_editor.AddVertexEvent;
import vg.services.plugin_manager.event_and_request.request.UIRequestAddTab;
import vg.shared.graph_editor.SharedGraphEditor;

public class JGraphRenderer extends AJGraphComponents {
	// Defines
	private static final String DEF_NAME_ATTRIBUTE = "name";
	private static final String DEF_SHAPE_ATTRIBUTE = "shape";
	private static final String DEF_COLOR_ATTRIBUTE = "color";
	
	// Style data
	private mxStylesheet styleSheet;
	
	private int	spacingLeft;
	private int	spacingTop;
	private int	spacingRight;
	private int	spacingBottom;
	
	private String fontColor;
	private String fillColor;
	private String vertexShape;
	private String graphElemetsFont;
	private String vertexTextAlign;
	
	// Main components
	private AGraphView parent;
	private mxGraph jgraph;
	private mxGraphComponent jgraph_view;
	private mxGraphOutline jgraph_minimap;
	
	private mxUndoManager jgraph_undo_manager;
	
	// Main data
	private Graph graph;
	private int graph_id, graph_model_id;
	
	private Map<mxCell, Vertex> cell_vertex; // Cell(4 Vertex) -> Vertex 
	private Map<mxCell, Edge> cell_edge;     // Cell(4 Edge) -> Edge 
	private Map<Vertex, mxCell> vertex_cell; // Vertex -> Cell(4 Vertex)
	private Map<Edge, mxCell> edge_cell;     // Edge -> Cell(4 Edge)
	private Map<Vertex, Map<Attribute, MutableBoolean>> vertex_attributes;
	private Map<Edge, Map<Attribute, MutableBoolean>> edge_attributes;
	
	private List<Observer> observers;
	
	private JPopupMenu popup;
	
	private boolean panning = false;

	
	public JGraphRenderer(AGraphView parent) {
		this.parent = parent;
		
		reset();
	}
	
	public void merge(JGraphRenderer src_jgraph_renderer, Graph srcGraph) {
		reset();
		
		// copy graph settings
		spacingLeft = src_jgraph_renderer.spacingLeft;
		spacingTop = src_jgraph_renderer.spacingTop;
		spacingRight = src_jgraph_renderer.spacingRight;
		spacingBottom = src_jgraph_renderer.spacingBottom;
		
		fontColor = src_jgraph_renderer.fontColor;
		fillColor = src_jgraph_renderer.fillColor;
		vertexShape = src_jgraph_renderer.vertexShape;
		graphElemetsFont = src_jgraph_renderer.graphElemetsFont;
		vertexTextAlign = src_jgraph_renderer.vertexTextAlign;

		// copy graph
		setDirected(srcGraph.isDirected());
		
		Object parent = jgraph.getDefaultParent();
		jgraph.getModel().beginUpdate();
		for (Vertex vertex : srcGraph.getVertices()) {
			mxCell v_src = src_jgraph_renderer.vertex_cell.get(vertex);
			mxCell v_dst = null;
			if (v_src != null) {
				store_attributes4vertex(vertex, src_jgraph_renderer.vertex_attributes.get(vertex));
				v_dst = (mxCell)jgraph.insertVertex(parent, v_src.getId(), v_src.getValue(), 
									   v_src.getGeometry().getX(), v_src.getGeometry().getY(), 
									   v_src.getGeometry().getWidth(), v_src.getGeometry().getHeight(), v_src.getStyle());
			} else {
				store_attributes4vertex(vertex);
				String title = generate_text4vertex(vertex);
				v_dst = (mxCell)jgraph.insertVertex(parent, Integer.toString(vertex.getStorableId()), title, 0, 0, 100, 30);
			}			
			vertex_cell.put(vertex, v_dst);
			cell_vertex.put(v_dst, vertex);
			graph.addVertex(vertex);
			auto_generate_style4vertex(vertex);
		}
		
		for (Edge edge : srcGraph.getEdges()) {
			mxCell e_src = src_jgraph_renderer.edge_cell.get(edge);
			mxCell e_dst = null;
			
			Object src = vertex_cell.get(edge.getSource());
			Object trg = vertex_cell.get(edge.getTarget());
			
			if (src == null || trg == null) continue;
			
			if (e_src != null) {
				store_attributes4edge(edge, src_jgraph_renderer.edge_attributes.get(edge));
				e_dst = (mxCell)jgraph.insertEdge(parent, e_src.getId(), e_src.getValue(), src, trg, e_src.getStyle());
				e_dst.setGeometry(e_src.getGeometry());
			} else {
				store_attributes4edge(edge);
				e_dst = (mxCell)jgraph.insertEdge(parent, Integer.toString(edge.getStorableId()), null, src, trg);
			}			
			edge_cell.put(edge, (mxCell)e_dst);
			cell_edge.put((mxCell)e_dst, edge);
			graph.addEdge(edge);
			auto_generate_style4edge(edge);
		}
		jgraph.getModel().endUpdate();
		
		// zoom
		double x = 0;
		double y = 0;

		Object[] cells = jgraph.getChildCells(jgraph.getDefaultParent());
		boolean initialize = false;
		for (Object cell : cells) {
			mxCellState state = jgraph.getView().getState(cell);
			if (!initialize) {
				x = state.getLabelBounds().getX();
				y = state.getLabelBounds().getY();
				initialize = true;
			}
			if (state.getLabelBounds().getX() < x)
				x = state.getLabelBounds().getX();

			if (state.getLabelBounds().getY() < y)
				y = state.getLabelBounds().getY();
		}
		if (initialize)
			jgraph.moveCells(jgraph.getChildCells(jgraph.getDefaultParent()), -x, -y);

		double zoomfactor = jgraph.getView().getScale();
		double zoomfactorx = zoomfactor / jgraph.getView().getGraphBounds().getWidth() * src_jgraph_renderer.jgraph_view.getWidth();
		double zoomfactory = zoomfactor / jgraph.getView().getGraphBounds().getHeight() * src_jgraph_renderer.jgraph_view.getHeight();

		jgraph.getView().setScale(Math.max(Math.min(zoomfactorx, zoomfactory), zoomfactor));
		refreshView();
	}
	
	public void reset() {
		graph = new Graph(false);
		
		vertex_cell = new HashMap<Vertex, mxCell>();
		edge_cell = new HashMap<Edge, mxCell>();
		cell_vertex = new HashMap<mxCell, Vertex>();
		cell_edge = new HashMap<mxCell, Edge>();
		
		vertex_attributes = new HashMap<Vertex, Map<Attribute,MutableBoolean>>();
		edge_attributes = new HashMap<Edge, Map<Attribute,MutableBoolean>>();
				
		gui_setup();
	}
	
//=============================================================================
//-----------------Methods for getting different views-------------------------
	public JComponent getView() {
		return jgraph_view;
	}
	
	public JComponent getMiniMap() {
		return jgraph_minimap;
	}
	
	public void refreshView() {
		apply_settings();
		mark_vertices_with_inner_graphs();
	}
	
//=============================================================================
//-----------------Methods for getting information about graph elements--------
	public Map<Edge, Map<Attribute, MutableBoolean>> getAllEdgesInfo() {
		return edge_attributes;
	}
	
	public Map<Vertex, Map<Attribute, MutableBoolean>> getAllVerticesInfo() {
		return vertex_attributes;
	}
	
	public Map<Edge, Map<Attribute, MutableBoolean>> getSelectionEdgesInfo() {
		HashMap<Edge, Map<Attribute, MutableBoolean>> map = new HashMap<Edge, Map<Attribute, MutableBoolean>>();
		Object[] cells;
		if (jgraph.isSelectionEmpty()) {
			cells = jgraph.getChildEdges(jgraph.getDefaultParent());
		} else {
			cells = jgraph.getSelectionCells();
		}

		for (Object cell : cells) {
			Edge e = cell_edge.get(cell);
			if (e != null)
				map.put(e, edge_attributes.get(e));
		}
		return map;
	}
	
	public Map<Vertex, Map<Attribute, MutableBoolean>> getSelectionVerticesInfo() {
		HashMap<Vertex, Map<Attribute, MutableBoolean>> map = new HashMap<Vertex, Map<Attribute, MutableBoolean>>();
		Object[] cells;
		if (jgraph.isSelectionEmpty()) {
			cells = jgraph.getChildVertices(jgraph.getDefaultParent());
		} else {
			cells = jgraph.getSelectionCells();
		}

		for (Object cell : cells) {
			Vertex v = cell_vertex.get(cell);
			if (v != null)
			map.put(v, vertex_attributes.get(v));
		}
		return map;
	}
	
	public Graph getGraph() {
		return graph.clone();
	}

	public Graph getSelectionSubGraph() {
		// clone vertices
		Object[] cells;
		if (jgraph.isSelectionEmpty()) {
			cells = jgraph.getChildVertices(jgraph.getDefaultParent());
		} else {
			cells = jgraph.getSelectionCells();
		}

		Map<Vertex, Vertex> new_vertices = new HashMap<Vertex, Vertex>();
		for (Object cell : cells) {
			Vertex v = cell_vertex.get(cell);
			if (v != null)
				new_vertices.put(v, v.clone());
		}
		
		// clone edges
		if (jgraph.isSelectionEmpty()) {
			cells = jgraph.getChildEdges(jgraph.getDefaultParent());
		} else {
			cells = jgraph.getSelectionCells();
		}
		
		Collection<Edge> new_edges = new ArrayList<Edge>();
		for (Object cell : cells) {
			Edge e = cell_edge.get(cell);
			if (e != null) {
				Vertex src = e.getSource();
				Vertex trg = e.getTarget();
				if (src != null && trg != null) {
					Vertex src_new = new_vertices.get(src);
					Vertex trg_new = new_vertices.get(trg);
					//if (src_new != null && trg_new != null)
						new_edges.add(new Edge(src_new, trg_new, ((AttributeItem)e).clone()));
				}
			}
		}

		Graph new_g = new Graph(new_vertices.values(), new_edges, graph.isDirected());
		//new_g.setGraphViewId(graph.getGraphViewId());
		new_g.setStorableId(graph.getStorableId());
		return new_g;
	}

//=============================================================================
//-----------------Methods for editing graph-----------------------------------
	public void setDirected(final boolean directed) {
		graph.setDirected(directed);
		refreshView();
	}
	
	public void addVertex(Vertex vertex) {
		if (vertex == null) return;
		
		Object parent = jgraph.getDefaultParent();
		
		jgraph.getModel().beginUpdate();
		try {
			store_attributes4vertex(vertex);
			String title = generate_text4vertex(vertex);
			Object v = jgraph.insertVertex(parent, Integer.toString(vertex.getStorableId()), title, 0, 0, 100, 30);
			vertex_cell.put(vertex, (mxCell)v);
			cell_vertex.put((mxCell)v, vertex);
			graph.addVertex(vertex);
			auto_generate_style4vertex(vertex);
			mark_vertex_with_inner_graph(vertex);
		} finally {
			jgraph.getModel().endUpdate();
		}
	}
	
	public void addVertices(Collection<Vertex> vertices) {
		if (vertices == null || vertices.isEmpty()) return;
		
		Object parent = jgraph.getDefaultParent();
		
		jgraph.getModel().beginUpdate();
		try {
			for (Vertex vertex : vertices) {
				store_attributes4vertex(vertex);
				String title = generate_text4vertex(vertex);
				Object v = jgraph.insertVertex(parent, Integer.toString(vertex.getStorableId()), title, 0, 0, 100, 30);
				vertex_cell.put(vertex, (mxCell)v);
				cell_vertex.put((mxCell)v, vertex);
				graph.addVertex(vertex);
				auto_generate_style4vertex(vertex);
				mark_vertex_with_inner_graph(vertex);
			}
		} finally {
			jgraph.getModel().endUpdate();
		}
	}
	
	public void addEdge(Edge edge) {
		if (edge == null) return;
		
		Object parent = jgraph.getDefaultParent();
		
		jgraph.getModel().beginUpdate();
		try {
			Object src = vertex_cell.get(edge.getSource());
			Object trg = vertex_cell.get(edge.getTarget());
			if (src != null && trg != null) {
				store_attributes4edge(edge);
				Object e = jgraph.insertEdge(parent, Integer.toString(edge.getStorableId()), null, src, trg);
				edge_cell.put(edge, (mxCell)e);
				cell_edge.put((mxCell)e, edge);
				graph.addEdge(edge);
				auto_generate_style4edge(edge);
			}
		} finally {
			jgraph.getModel().endUpdate();
		}
	}
	
	public void addEdges(Collection<Edge> edges) {
		if (edges == null || edges.isEmpty()) return;
		
		Object parent = jgraph.getDefaultParent();
		
		jgraph.getModel().beginUpdate();
		try {
			for (Edge edge : edges) {
				Object src = vertex_cell.get(edge.getSource());
				Object trg = vertex_cell.get(edge.getTarget());
				if (src != null && trg != null) {
					store_attributes4edge(edge);
					Object e = jgraph.insertEdge(parent, Integer.toString(edge.getStorableId()), null, src, trg);
					edge_cell.put(edge, (mxCell)e);
					cell_edge.put((mxCell)e, edge);
					graph.addEdge(edge);
					auto_generate_style4edge(edge);
				}
			}
		} finally {
			jgraph.getModel().endUpdate();
		}
	}
	
	public void addAttribute4Vertex(Attribute attr, Vertex vertex) {
		mxCell cell = vertex_cell.get(vertex);
		
		if (cell != null && vertex != null && attr != null) {
			vertex.addAttribute(attr);
			store_attribute4vertex(vertex, attr);
			String text = generate_text4vertex(vertex);
			cell.setValue(text);
			// m.b. need call repaint
			jgraph_view.refresh();
		} 
	}
	
	public void addAttribute4Edge(Attribute attr, Edge edge) {
		mxCell cell = edge_cell.get(edge);
		
		if (cell != null && edge != null && attr != null) {
			edge.addAttribute(attr);
			store_attribute4edge(edge, attr);
			String text = generate_text4edge(edge);
			cell.setValue(text);
			// m.b. need call repaint
			jgraph_view.refresh();
		} 
	}

	public int getGraphId() {
		return graph_id;
	}
	
	public void setGraphId(int graphId) {
		this.graph_id = graphId;
	}
	
	public int getGraphModelId() {
		return graph_model_id;
	}
	
	public void setGraphModelId(int graphModelId) {
		this.graph_model_id = graphModelId;
	}
	
//=============================================================================
//-----------------Undo/Redo methods-------------------------------------------
	public void undo() {
		jgraph_undo_manager.undo();
	}
	
	public void redo() {
		jgraph_undo_manager.redo();
	}
	
//=============================================================================
//-----------------Panning-----------------------------------------------------
	public boolean isPanning() {
		return panning;
	};
	
	public void setPanning(boolean panning) {
		this.panning = panning;
	}
	
//=============================================================================
//-----------------Methods for adding observers to graph representation--------		
	public boolean addObserver(Observer o) {
		return observers.add(o);
	}

	public void removeAllObservers() {
		observers.clear();
	}

	public boolean removeObserver(Observer o) {
		observers.remove(o);
		return true;
	}
	
	public void addPopupMenuItem(JMenuItem item) {
		popup.add(item);
	}
	
	public void removePopupMenuItem(JMenuItem item) {
		popup.remove(item);
	}

//=============================================================================
//-----------------Methods for scaling graph-----------------------------------
	public void zoomIn() {
		jgraph_view.zoomIn();
	}

	public void zoomOut() {
		jgraph_view.zoomOut();		
	}
	
//=============================================================================
//-----------------Methods for changing visualization from elements------------
	public void setInvisibleAllEdgesBut(Set<Integer> edgeIds) {
		Object parent = jgraph.getDefaultParent();
		Object[] cells = jgraph.getChildEdges(parent);
		ArrayList<Object> visiblecells = new ArrayList<Object>();
		ArrayList<Object> invisiblecells = new ArrayList<Object>();
		for (Object cell : cells) {
			Edge e = cell_edge.get(cell);
			if (e != null) {
				if (edgeIds.contains(e.getStorableId()) && e.getStorableId() >= 0) {
					visiblecells.add(cell);
				} else {
					invisiblecells.add(cell);
				}
			}
		}
		
		mxUtils.setCellStyles(jgraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_OPACITY, "20");
		mxUtils.setCellStyles(jgraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "20");
		mxUtils.setCellStyles(jgraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_OPACITY, "100");
		mxUtils.setCellStyles(jgraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "100");
	}
	
	public void setInvisibleAllVerticesBut(Set<Integer> vertexIds) {
		Object parent = jgraph.getDefaultParent();
		Object[] cells = jgraph.getChildVertices(parent);
		ArrayList<Object> visiblecells = new ArrayList<Object>();
		ArrayList<Object> invisiblecells = new ArrayList<Object>();
		for (Object cell : cells) {
			Vertex v = cell_vertex.get(cell);
			if (v != null) {
				if (vertexIds.contains(v.getStorableId()) && v.getStorableId() >= 0) {
					visiblecells.add(cell);
				} else {
					invisiblecells.add(cell);
				}
			}
		}
		
		mxUtils.setCellStyles(jgraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_OPACITY, "20");
		mxUtils.setCellStyles(jgraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "20");
		mxUtils.setCellStyles(jgraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_OPACITY, "100");
		mxUtils.setCellStyles(jgraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "100");		
	}
	
	public void setInvisibleEdges(Set<Integer> edgeIds) {
		Object parent = jgraph.getDefaultParent();
		Object[] cells = jgraph.getChildEdges(parent);
		ArrayList<Object> visiblecells = new ArrayList<Object>();
		ArrayList<Object> invisiblecells = new ArrayList<Object>();
		for (Object cell : cells) {
			Edge e = cell_edge.get(cell);
			if (e != null) {
				if (edgeIds.contains(e.getStorableId()) && e.getStorableId() >= 0) {
					invisiblecells.add(cell);
				} else {
					visiblecells.add(cell);
				}
			}
		}
		
		mxUtils.setCellStyles(jgraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_OPACITY, "20");
		mxUtils.setCellStyles(jgraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "20");
		mxUtils.setCellStyles(jgraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_OPACITY, "100");
		mxUtils.setCellStyles(jgraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "100");
	}
	
	public void setInvisibleVertices(Set<Integer> vertexIds) {
		Object parent = jgraph.getDefaultParent();
		Object[] cells = jgraph.getChildVertices(parent);
		ArrayList<Object> visiblecells = new ArrayList<Object>();
		ArrayList<Object> invisiblecells = new ArrayList<Object>();
		for (Object cell : cells) {
			Vertex v = cell_vertex.get(cell);
			if (v != null) {
				if (vertexIds.contains(v.getStorableId()) && v.getStorableId() >= 0) {
					invisiblecells.add(cell);
				} else {
					visiblecells.add(cell);
				}
			}
		}
		
		mxUtils.setCellStyles(jgraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_OPACITY, "20");
		mxUtils.setCellStyles(jgraph.getModel(), invisiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "20");
		mxUtils.setCellStyles(jgraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_OPACITY, "100");
		mxUtils.setCellStyles(jgraph.getModel(), visiblecells.toArray(), mxConstants.STYLE_TEXT_OPACITY, "100");				
	}
	
	public void setSelectionElements(Set<Integer> lvsids, Set<Integer> lesids) {
		ArrayList<Object> selection = new ArrayList<Object>();
		Object parent = jgraph.getDefaultParent();
		if (lvsids != null) {
			for (Object cell : jgraph.getChildVertices(parent)) {
				Vertex v = cell_vertex.get(cell);
				if (v != null) {
					if (lvsids.contains(v.getStorableId()) && v.getStorableId() >= 0) {
						selection.add(cell);
					}
				}
			}
		}
		if (lesids != null) {
			for (Object cell : jgraph.getChildEdges(parent)) {
				Edge e = cell_edge.get(cell);
				if (e != null) {
					if (lesids.contains(e.getStorableId()) && e.getStorableId() >= 0) {
						selection.add(cell);
					}
				}
			}
		}
		jgraph.setSelectionCells(selection);
	}
	
	public void showAttributes(Set<String> vertexAttr, Set<String> edgeAttr) {
		Object[] cells;
		if (jgraph.isSelectionEmpty()) {
			cells = jgraph.getChildCells(jgraph.getDefaultParent());
		} else {
			cells = jgraph.getSelectionCells();
		}
		
		jgraph.getModel().beginUpdate();

		for (Object cell : cells) {
			// vertices
			Vertex v = cell_vertex.get(cell);
			if (v != null) {
				Map<Attribute, MutableBoolean> map = vertex_attributes.get(v);
				for (Attribute attr : map.keySet()) {
					String name = attr.getName();
					if (name != null && vertexAttr.contains(name)) {
						map.get(attr).setValue(true);
					} else {
						map.get(attr).setValue(false);
					}
				}
				String text = generate_text4vertex(v);
				((mxCell) cell).setValue(text);
			}
			
			mxCellState cellState = jgraph.getView().getState(cell);
			if(cellState != null) {
				cellState.setStyle(jgraph.getCellStyle(cell));
			} else {
				MainManager.log.printError("[" + this.getClass().getName() + ".showAttributes()] Cell state is null.");
			}
			
			// edges
			Edge e = cell_edge.get(cell);
			if (e != null) {
				Map<Attribute, MutableBoolean> map = edge_attributes.get(e);
				for (Attribute attr : map.keySet()) {
					String name = attr.getName();
					if (name != null && edgeAttr.contains(name)) {
						map.get(attr).setValue(true);
					} else {
						map.get(attr).setValue(false);
					}
				}
				String text = generate_text4edge(e);
				((mxCell) cell).setValue(text);
			}
		}
		
		jgraph.getModel().endUpdate();

		double x = 0;
		double y = 0;
		cells = jgraph.getChildCells(jgraph.getDefaultParent());
		for (Object cell : cells) {
			mxCellState state = jgraph.getView().getState(cell);
			if (state != null) {
				jgraph.getView().updateLabelBounds(state);
				if (state.getLabelBounds().getX() < x)
					x = state.getLabelBounds().getX();
				if (state.getLabelBounds().getY() < y)
					y = state.getLabelBounds().getY();
			}
		}
		
		if (x < 0 || y < 0) {
			final mxPoint translate = new mxPoint(-x, -y);
			jgraph.moveCells(jgraph.getChildCells(jgraph.getDefaultParent()), translate.getX(), translate.getY());
			jgraph.repaint();
		}
		
		// apply settings
		apply_settings();
		
		// m.b. need call repaint
		jgraph_view.refresh();
	}
	
	public int setColor4Vertex(String c, Vertex v) {
		jgraph.getModel().beginUpdate();
		int re = set_color4vertex(c, v);
		jgraph.getModel().endUpdate();
		
		// m.b. need call repaint
		jgraph_view.refresh();
		return re;
	}
	
	public int setColor4Edge(String c, Edge e) {
		jgraph.getModel().beginUpdate();
		int re = set_color4edge(c, e);
		jgraph.getModel().endUpdate();
		
		// m.b. need call repaint
		jgraph_view.refresh();
		return re;
	}
	
	public int setShape4Vertex(String shape, Vertex v) {
		jgraph.getModel().beginUpdate();
		int re = set_shape4vertex(shape, v);
		jgraph.getModel().endUpdate();
		
		// m.b. need call repaint
		jgraph_view.refresh();
		return re;
	}
	
	public int setShape4Edge(String shape, Edge e) {
		jgraph.getModel().beginUpdate();
		int re = set_shape4edge(shape, e);
		jgraph.getModel().endUpdate();
		
		// m.b. need call repaint
		jgraph_view.refresh();
		return re;
	}
	
	public void setStyle(String style, Object value) {
		set_element_style_without_applying(style, value);
		apply_settings();
	}
	
	public void setStyle(Map<String, Object> stylesheet) {
		if (stylesheet == null) return;
		
		for (String key : stylesheet.keySet()) {
			set_element_style_without_applying(key, stylesheet.get(key));
		}
		
		apply_settings();
	}
	
//=============================================================================
//-----------------Methods for algorithms--------------------------------------
	public void showCriticalPath(Vertex source, Vertex target, final boolean maximum, final String attrName) {
		// initialize from and to
		mxCell from = vertex_cell.get(source);
		mxCell to = vertex_cell.get(target);
		if (from == null || to == null) return;
		
		Object parent = jgraph.getDefaultParent();
		mxGraphAnalysis mga = mxGraphAnalysis.getInstance();
		Object[] path = mga.getShortestPath(jgraph, from, to, new mxICostFunction() {
			@Override
			public double getCost(mxCellState state) {
				double length = 0;
				boolean attrExist = false;
				
				if (cell_edge.get(state.getCell()) == null)
					return Double.POSITIVE_INFINITY;
				
				for (Attribute attr : cell_edge.get(state.getCell()).getAttributes()) {
					if (attrName.equals(attr.getName())) {
						length = Double.valueOf(attr.getValue());
						attrExist = true;
					}
				}
				if (!attrExist)
					return Double.POSITIVE_INFINITY;
				if (maximum)
					return 1 / length;
				else
					return length;
			}
		}, jgraph.getChildEdges(parent).length, graph.isDirected());

		if (path.length > 0) {
			jgraph.setSelectionCells(path);
		} else {
			MainManager.windowMessage.infoMessage("Critical path was not found between vertexes " + source.getStorableId() + " to " + target.getStorableId(), "Critical path messsage");
		}
		jgraph.setSelectionCells(path);
	}
	
	public void showCycles(final Vertex v) {
		ArrayList<Object> cicle = findPath(v, v);
		if (cicle != null && cicle.size() > 1) {
			jgraph.setSelectionCells(cicle);
		} else {
			MainManager.windowMessage.infoMessage("Cicle was not found with vertex " + v.getStorableId(), "Cicle messsage");
		}
	}
	
	public void showPaths(final Vertex source, final Vertex target) {
		ArrayList<Object> paths = findPath(source, target);
		if (paths != null && paths.size() > 0) {
			jgraph.setSelectionCells(paths);
		} else {
			MainManager.windowMessage.infoMessage("Path was not found beetween vertexes " + source.getStorableId() + " to " + target.getStorableId(), "Path messsage");
		}
	}
	
//=============================================================================
//-----------------Implementation of AJGraphComponents-------------------------
	@Override
	public mxGraph getMxGraph() {
		return jgraph;
	}
	
	@Override
	public mxGraphComponent getMxGraphComponent() {
		return jgraph_view;
	}
	
//=============================================================================
//-----------------PRIVATE METHODS---------------------------------------------
	
	// Initializing of mxGraph and mxGraphComponent
	// Enabling/Disabling of options for graph
	// Adding event handlers
	private void gui_setup() {
		jgraph = new mxGraph();
		jgraph_undo_manager = new mxUndoManager();
		jgraph_view = new mxGraphComponent(jgraph) {
			private static final long serialVersionUID = 1L;
			public boolean isPanningEvent(MouseEvent event) {
				return (event != null) ? (event.getButton() == MouseEvent.BUTTON2 || panning ||
						(event.isShiftDown() && event.isControlDown())) : false;
			}			
		};
		observers = new ArrayList<Observer>();

		// deny clone cells
		jgraph.setCellsCloneable(false);
		jgraph.setGridEnabled(false);
		jgraph.setCellsDeletable(false);
		jgraph.setHtmlLabels(true);
		jgraph.setMultigraph(true);
		// deny changing edge targets
		jgraph.setCellsDisconnectable(false);
		// deny edge which doesn't connect with some cells
		jgraph.setAllowDanglingEdges(false);
		jgraph.setCellsEditable(false);
		jgraph.setGridSize(4);
		
		// adding event handlers
		jgraph_view.setConnectable(false);
		jgraph_view.setToolTips(true);
		jgraph_view.setDragEnabled(false);
		jgraph_view.setZoomFactor(2);
		// allow rubberband selection
		new mxRubberband(jgraph_view);
		// allow use keyboard
		new mxKeyboardHandler(jgraph_view);

		// minimap
		jgraph_minimap = new mxGraphOutline(jgraph_view);
		MouseWheelListener wheelTracker = new MouseWheelListener() {

			public void mouseWheelMoved(MouseWheelEvent e) {
				if (e.getSource() instanceof mxGraphOutline || e.isControlDown()) {
					if (e.getWheelRotation() < 0) {
						jgraph_view.zoomIn();
					} else {
						jgraph_view.zoomOut();
					}
				}

			}

		};
		jgraph_minimap.addMouseWheelListener(wheelTracker);
		jgraph_view.addMouseWheelListener(wheelTracker);

		// undo/redo event handlers
		mxIEventListener undoHandler = new mxIEventListener() {
			public void invoke(Object sender, mxEventObject evt) {
				List<mxUndoableChange> changes = ((mxUndoableEdit) evt.getProperty("edit")).getChanges();
				jgraph.setSelectionCells(jgraph.getSelectionCellsForChanges(changes));

			}
		};
		
		mxIEventListener undoHandle = new mxIEventListener() {
			public void invoke(Object sender, mxEventObject evt) {
				jgraph_undo_manager.undoableEditHappened((mxUndoableEdit) evt.getProperty("edit"));
			}
		};
		
		jgraph.getModel().addListener(mxEvent.UNDO, undoHandle);
		jgraph.getView().addListener(mxEvent.UNDO, undoHandle);
		
		jgraph_undo_manager.addListener(mxEvent.UNDO, undoHandler);
		jgraph_undo_manager.addListener(mxEvent.REDO, undoHandler);
		
		// selection handler
		mxIEventListener selectHandler = new mxIEventListener() {
			public void invoke(Object sender, mxEventObject evt) {
				turn_observers();
			}
		};
		jgraph.getSelectionModel().addListener(mxEvent.CHANGE, selectHandler);
		
		// move handler
		mxIEventListener moveHandler = new mxIEventListener() {
			public void invoke(Object sender, mxEventObject evt) {
				double x = 0, y = 0;
				for (Object o : (Object[]) evt.getProperty("cells")) {
					mxCell cell = (mxCell) o;
					if (cell.getGeometry().getX() < x)
						x = cell.getGeometry().getX();
					if (cell.getGeometry().getY() < y)
						y = cell.getGeometry().getY();
				}
				if (x != 0 || y != 0)
					jgraph.moveCells(jgraph.getChildCells(jgraph.getDefaultParent()), -x, -y);
			}
		};
		
		jgraph.addListener(mxEvent.CELLS_MOVED, moveHandler);
		
		// popup menu
		popup = new JPopupMenu();
		final JMenuItem openSubGraphMenuItem = new JMenuItem("Open subgraph in new tab");
		openSubGraphMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				AGraphView subgraph = MainManager.graphViewManager.newGraphView(parent, getSelectionSubGraph());
				UIRequestAddTab r = new UIRequestAddTab(subgraph, null);
				MainManager.pluginParameter.userInterface.addRequest(r);
			}
		});
		
		final JMenuItem addVertexMenuItem = new JMenuItem("Add vertex");
		addVertexMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				int vertexId = MainManager.model.createVertexHeader(graph_id, -1);
				Vertex v = MainManager.model.getVertex(vertexId);
				addVertex(v);
				AddVertexEvent event = new AddVertexEvent(v);
				MainManager.view.addEvent(event);
			}
		});
		
		final JMenuItem addEdgeMenuItem = new JMenuItem("Add edge");
		addEdgeMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				Object[] cells;
				cells = jgraph.getSelectionCells();

				if (cells != null && cells.length == 2) {
					Vertex src = cell_vertex.get(cells[0]);
					Vertex trg = cell_vertex.get(cells[1]);
					if (src != null && trg != null) {
						int edgeId = MainManager.model.createEdgeHeader(graph_id, src.getStorableId(), trg.getStorableId());
						Edge e = MainManager.model.getEdge(edgeId);
						addEdge(e);
						AddEdgeEvent event = new AddEdgeEvent(e);
						MainManager.view.addEvent(event);
					}
				}
                if (cells != null && cells.length == 1) {
                    Vertex src = cell_vertex.get(cells[0]);
                    Vertex trg = cell_vertex.get(cells[0]);
                    if (src != null && trg != null) {
                        int edgeId = MainManager.model.createEdgeHeader(graph_id, src.getStorableId(), trg.getStorableId());
                        Edge e = MainManager.model.getEdge(edgeId);
                        addEdge(e);
                        AddEdgeEvent event = new AddEdgeEvent(e);
                        MainManager.view.addEvent(event);
                    }
                }
            }
		});
		
		final JMenuItem addAttributeMenuItem = new JMenuItem("Add attribute");
		addAttributeMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				Object[] cells;
				cells = jgraph.getSelectionCells();

				if (cells != null && cells.length == 1) {
					Attribute attr = null;
					Vertex vertex = cell_vertex.get(cells[0]);
					if (vertex != null) {
						attr = SharedGraphEditor.addAttribute4Vertex(vertex);
						addAttribute4Vertex(attr, vertex);
					} 
					
					Edge edge =  cell_edge.get(cells[0]);
					if (edge != null) {
						attr = SharedGraphEditor.addAttribute4Edge(edge);
						addAttribute4Edge(attr, edge);					
					}
					
					if (attr != null) {
						AddAttributeEvent event = new AddAttributeEvent(attr);
						MainManager.view.addEvent(event);
					}
				}				
			}
		});
		
		popup.add(openSubGraphMenuItem);
		popup.add(addVertexMenuItem);
		popup.add(addEdgeMenuItem);
		popup.add(addAttributeMenuItem);
		
		jgraph_view.getGraphControl().addMouseListener(new MouseAdapter() {
			private void popupTriger(MouseEvent e) {
				if (e.isPopupTrigger()) {
					openSubGraphMenuItem.setEnabled(true);

					mxCell cell = (mxCell) jgraph_view.getCellAt(e.getX(), e.getY());
					popup.show(e.getComponent(), e.getX(), e.getY());
				}
				SwingUtilities.updateComponentTreeUI(popup);
			}

			@Override
			public void mouseReleased(MouseEvent e) {
				if (e.getClickCount() > 1) {
					mxCell cell = (mxCell) jgraph_view.getCellAt(e.getX(), e.getY());
					if (cell != null) {
						Vertex v = cell_vertex.get(cell);
						if (v != null) {
							Graph g = v.getInnerGraph();
							if (g != null) {
								int storableId = g.getStorableId();
								if (storableId >= 0)
									g = MainManager.model.getGraph(storableId);
								if (g != null && !g.isEmpty()) {
									AGraphView view = new SmartGraphView(g);
									UIRequestAddTab r = new UIRequestAddTab(view, null);
									MainManager.pluginParameter.userInterface.addRequest(r);
								}
							}
						}
					}
				}
				popupTriger(e);
			}

			@Override
			public void mousePressed(MouseEvent e) {
				popupTriger(e);
			}
		});
		
		// apply graph settings
		init_graph_settings();
		apply_settings();
		
	}
	
	private void store_attributes4vertex(Vertex vertex) {
		if (vertex == null) return;
		
		List<Attribute> attributes = vertex.getAttributes();
		if (attributes == null) attributes = Collections.emptyList();
		
		Map<Attribute, MutableBoolean> map = new HashMap<Attribute, MutableBoolean>(attributes.size());
		for (Attribute buf : attributes) {
			String name = buf.getName();
			if (name == null) continue;
			
			if (name.equals(DEF_NAME_ATTRIBUTE)) {
				map.put(buf, new MutableBoolean(true));
			} else {
				map.put(buf, new MutableBoolean(false));
			}
		}
		vertex_attributes.put(vertex, map);
	}
	
	// Store attribute and make it visible
	private void store_attribute4vertex(Vertex vertex, Attribute attr) {
		if (vertex == null || attr == null) return;
		
		Map<Attribute, MutableBoolean> map = vertex_attributes.get(vertex);
		if (map == null)
			map = new HashMap<Attribute, MutableBoolean>();
		
		String name = attr.getName();
		String value = attr.getValue();
		Class<?> type = attr.getType();
		if (name == null || value == null || type == null) return;
		
		map.remove(attr);
		map.put(attr, new MutableBoolean(true));
		
		vertex_attributes.put(vertex, map);
	}
	
	private void store_attributes4edge(Edge edge) {
		if (edge == null) return;
		
		List<Attribute> attributes = edge.getAttributes();
		if (attributes == null) attributes = Collections.emptyList();
		
		Map<Attribute, MutableBoolean> map = new HashMap<Attribute, MutableBoolean>(attributes.size());
		for (Attribute buf : attributes) {
			if (buf.getName() == null) continue;
			
			map.put(buf, new MutableBoolean(false));
		}
		edge_attributes.put(edge, map);
	}
	
	// Store attribute and make it visible
	private void store_attribute4edge(Edge edge, Attribute attr) {
		if (edge == null || attr == null) return;
		
		Map<Attribute, MutableBoolean> map = edge_attributes.get(edge);
		if (map == null)
			map = new HashMap<Attribute, MutableBoolean>();
		
		String name = attr.getName();
		String value = attr.getValue();
		Class<?> type = attr.getType();
		if (name == null || value == null || type == null) return;
		
		map.remove(attr);
		map.put(attr, new MutableBoolean(true));
		
		edge_attributes.put(edge, map);
	}
	
	private void store_attributes4vertex(Vertex vertex, Map<Attribute, MutableBoolean> to) {
		if (vertex == null) return;
		
		List<Attribute> attributes = vertex.getAttributes();
		if (attributes == null) attributes = Collections.emptyList();
		
		Map<Attribute, MutableBoolean> map = new HashMap<Attribute, MutableBoolean>(attributes.size());
		for (Attribute buf : attributes) {
			String name = buf.getName();
			if (name == null) continue;
			
			boolean check = false;
			for (Attribute to_buf : to.keySet()) {
				String to_name = to_buf.getName();
				if (to_name != null && to_name.equals(name)) {
					map.put(buf, new MutableBoolean(to.get(to_buf).booleanValue()));
					check = true;
					break;
				}
			}
			
			if (check == false) {
				map.put(buf, new MutableBoolean(false));
			}			
		}
		vertex_attributes.put(vertex, map);
	}
	
	private void store_attributes4edge(Edge edge, Map<Attribute, MutableBoolean> to) {
		if (edge == null) return;
		
		List<Attribute> attributes = edge.getAttributes();
		if (attributes == null) attributes = Collections.emptyList();
		
		Map<Attribute, MutableBoolean> map = new HashMap<Attribute, MutableBoolean>(attributes.size());
		for (Attribute buf : attributes) {
			String name = buf.getName();
			if (name == null) continue;
			
			boolean check = false;
			for (Attribute to_buf : to.keySet()) {
				String to_name = to_buf.getName();
				if (to_name != null && to_name.equals(name)) {
					map.put(buf, new MutableBoolean(to.get(to_buf).booleanValue()));
					check = true;
					break;
				}
			}
			
			if (check == false) {
				map.put(buf, new MutableBoolean(false));
			}			
		}
		edge_attributes.put(edge, map);
	}
	
	private String generate_text4vertex(Vertex vertex) {
		Map<Attribute, MutableBoolean> map = vertex_attributes.get(vertex);
		
		if (map == null) return "";
		
		StringBuilder title = new StringBuilder(100);
				
		for (Attribute attr : map.keySet()) {
			String name = attr.getName();
			String value = attr.getValue();
			if (name == null)
				continue;
			if (value == null) 
				value = "";

            if (map.get(attr).isFalse())
                continue;
			
			if (name.equals(DEF_NAME_ATTRIBUTE)) {
				StringBuilder newTitle = new StringBuilder(title.length() + 100);
				newTitle.append("<b>");
				newTitle.append(value.replaceAll("\\n", "<br>"));
				newTitle.append("</b><br>");
				newTitle.append(title);
				title = newTitle;
			} else {
				title.append("<b>");
				title.append(name);
				title.append("</b>");
				title.append(":");
				title.append(value.replaceAll("\\n", "<br>"));
				title.append("<br>");
			}
		}
		
		if (title.length() > 0) {
			title.insert(0, "<html>");
			title.append("</html>");
		}
		
		return title.toString();
	}
	
	private void auto_generate_style4vertex(Vertex vertex) {
		Map<Attribute, MutableBoolean> map = vertex_attributes.get(vertex);
		
		if (map == null) return;
		
		for (Attribute attr : map.keySet()) {
			String name = attr.getName();
			String value = attr.getValue();
			if (name == null)
				continue;
			if (value == null) 
				continue;

			if (name.equals(DEF_SHAPE_ATTRIBUTE)) {
				set_shape4vertex(value, vertex);
			} else if (name.equals(DEF_COLOR_ATTRIBUTE)) {
				set_color4vertex(value, vertex);				
			}
		}
	}
	
	private void auto_generate_style4edge(Edge edge) {
		Map<Attribute, MutableBoolean> map = vertex_attributes.get(edge);
		
		if (map == null) return;
		
		for (Attribute attr : map.keySet()) {
			String name = attr.getName();
			String value = attr.getValue();
			if (name == null)
				continue;
			if (value == null) 
				continue;

			if (name.equals(DEF_SHAPE_ATTRIBUTE)) {
				set_shape4edge(value, edge);
			} else if (name.equals(DEF_COLOR_ATTRIBUTE)) {
				set_color4edge(value, edge);				
			}
		}
	}

	private String generate_text4edge(Edge edge) {
		Map<Attribute, MutableBoolean> map = edge_attributes.get(edge);
		
		if (map == null) return "";
		
		StringBuilder title = new StringBuilder(100);
		for (Attribute attr : map.keySet()) {
			String name = attr.getName();
			String value = attr.getValue();
			if (name == null)
				continue;
			if (value == null) 
				value = "";

            if (map.get(attr).isFalse())
                continue;

            title.append("<b>");
			title.append(name);
			title.append("</b>");
			title.append(":");
			title.append(value.replaceAll("\\n", "<br>"));
			title.append("<br>");
		}
		
		if (title.length() > 0) {
			title.insert(0, "<html>");
			title.append("</html>");
		}
		
		return title.toString();
	}
	
	private void mark_vertices_with_inner_graphs() {
		jgraph.getModel().beginUpdate();		
		for (Vertex vertex : vertex_cell.keySet()) {
			Graph innerGraph = vertex.getInnerGraph(); 
			if (innerGraph != null && innerGraph.getStorableId() >= 0) {
				mxCell cellVertex = vertex_cell.get(vertex);
				cellVertex.setStyle(cellVertex.getStyle() + ";" + mxConstants.STYLE_FONTSTYLE + "=" + mxConstants.FONT_UNDERLINE + ";" + mxConstants.STYLE_FONTCOLOR + "=" + "blue");
			}
		}
		jgraph.getModel().endUpdate();
	}
	
	// Use only between beginUpdate() mark_vertex_with_inner_graph(v) endUpdate()
	private void mark_vertex_with_inner_graph(Vertex vertex) {
		Graph innerGraph = vertex.getInnerGraph(); 
		if (innerGraph != null && innerGraph.getStorableId() >= 0) {
			mxCell cellVertex = vertex_cell.get(vertex);
			cellVertex.setStyle(cellVertex.getStyle() + ";" + mxConstants.STYLE_FONTSTYLE + "=" + mxConstants.FONT_UNDERLINE + ";" + mxConstants.STYLE_FONTCOLOR + "=" + "blue");
		}
	}
	
	private void turn_observers() {
		final Object[] arrLocal = observers.toArray();

		Thread t = new Thread(new Runnable() {
			
			@Override
			public void run() {
				for (int i = 0; i < arrLocal.length; i++) {
					((Observer) arrLocal[i]).update(null, null);
				}				
			}
		});
		t.start();
	}
	
	public int set_color4vertex(String c, Vertex v) {
		mxCell cell = vertex_cell.get(v);
		if (cell != null && check_color(c)) {
			cell.setStyle(cell.getStyle() + ";" + mxConstants.STYLE_FILLCOLOR + "=" + c);
			return 0;
		}
		
		return -1;
	}
	
	public int set_color4edge(String c, Edge e) {
		mxCell cell = edge_cell.get(e);
		if (cell != null && check_color(c)) {
			cell.setStyle(cell.getStyle() + ";" + mxConstants.STYLE_FILLCOLOR + "=" + c);
			return 0;
		}
		
		return -1;		
	}
	
	public int set_shape4vertex(String shape, Vertex v) {
		mxCell cell = vertex_cell.get(v);
		if (cell != null && check_shape(shape)) {
			cell.setStyle(cell.getStyle() + ";" + mxConstants.STYLE_SHAPE + "=" + shape);
			return 0;
		}
		
		return -1;
	}
	
	public int set_shape4edge(String shape, Edge e) {
		mxCell cell = edge_cell.get(e);
		if (cell != null && check_shape(shape)) {
			cell.setStyle(cell.getStyle() + ";" + mxConstants.STYLE_SHAPE + "=" + shape);
			return 0;
		}
		
		return -1;		
	}
	
	private boolean check_color(String color) {
		try {
			mxUtils.parseColor(color);
			return true;
		} catch (Throwable ex) {
			return false;
		}
	}
	
	private boolean check_shape(String shape) {
		if (mxConstants.SHAPE_RECTANGLE.equals(shape) ||
			mxConstants.SHAPE_ELLIPSE.equals(shape) ||
			mxConstants.SHAPE_DOUBLE_ELLIPSE.equals(shape) ||
			mxConstants.SHAPE_RHOMBUS.equals(shape) ||
			mxConstants.SHAPE_LINE.equals(shape) ||
			mxConstants.SHAPE_IMAGE.equals(shape) ||
			mxConstants.SHAPE_ARROW.equals(shape) ||
			mxConstants.SHAPE_CURVE.equals(shape) ||
			mxConstants.SHAPE_LABEL.equals(shape) ||
			mxConstants.SHAPE_CYLINDER.equals(shape) ||
			mxConstants.SHAPE_SWIMLANE.equals(shape) ||
			mxConstants.SHAPE_CONNECTOR.equals(shape) ||
			mxConstants.SHAPE_ACTOR.equals(shape) ||
			mxConstants.SHAPE_CLOUD.equals(shape) ||
			mxConstants.SHAPE_TRIANGLE.equals(shape) ||
			mxConstants.SHAPE_HEXAGON.equals(shape)) {
			return true;
		}
		return false;
	}

//=============================================================================
//-----------------Methods for working with graph settings---------------------
	public void set_element_style_without_applying(String style, Object value) {
		if (style == null || value == null) return;
		
		try {
			if (style.equals(IGraphSettings.DEF_STYLE_VERTEX_SPACING_LEFT))
				spacingLeft = (Integer)value;
			if (style.equals(IGraphSettings.DEF_STYLE_VERTEX_SPACING_RIGHT))
				spacingRight = (Integer)value;
			if (style.equals(IGraphSettings.DEF_STYLE_VERTEX_SPACING_TOP))
				spacingTop = (Integer)value;
			if (style.equals(IGraphSettings.DEF_STYLE_VERTEX_SPACING_BOTTOM))
				spacingBottom = (Integer)value;
			if (style.equals(IGraphSettings.DEF_STYLE_GRAPH_ELEMENTS_FONT))
				graphElemetsFont = (String)value;
			if (style.equals(IGraphSettings.DEF_STYLE_VERTEX_FILL_COLOR))
				fillColor = (String)value;
			if (style.equals(IGraphSettings.DEF_STYLE_VERTEX_FONT_COLOR))
				fontColor = (String)value;
			if (style.equals(IGraphSettings.DEF_STYLE_VERTEX_SHAPE))
				vertexShape = (String)value;
			if (style.equals(IGraphSettings.DEF_STYLE_VERTEX_TEXT_ALIGN))
				vertexTextAlign = (String)value;
		} catch (Throwable ex) {
			MainManager.log.printException(ex);
		}
	}
	
	private void apply_settings() {
		Map<String, Object> style = styleSheet.getDefaultVertexStyle();

		style.put(mxConstants.STYLE_SHAPE, vertexShape);
		style.put(mxConstants.STYLE_FONTCOLOR, fontColor);
		style.put(mxConstants.STYLE_FILLCOLOR, fillColor);
		style.put(mxConstants.STYLE_SPACING_LEFT, spacingLeft);
		style.put(mxConstants.STYLE_SPACING_RIGHT, spacingRight);
		style.put(mxConstants.STYLE_SPACING_TOP, spacingTop);
		style.put(mxConstants.STYLE_SPACING_BOTTOM, spacingBottom);
		style.put(mxConstants.STYLE_ALIGN, vertexTextAlign);
		style.put(mxConstants.STYLE_FONTFAMILY, graphElemetsFont);
		
		style = styleSheet.getDefaultEdgeStyle();
		
		style.put(mxConstants.STYLE_FONTFAMILY, graphElemetsFont);
		if (graph.isDirected())
			style.put(mxConstants.STYLE_ENDARROW, mxConstants.ARROW_CLASSIC);
		else
			style.put(mxConstants.STYLE_ENDARROW, mxConstants.NONE);
		style.put(mxConstants.STYLE_ROUNDED, "1");
		
		// apply settings
		jgraph.setStylesheet(styleSheet);
		Object parent = jgraph.getDefaultParent();
		jgraph.getModel().beginUpdate();
		for (Object cell : jgraph.getChildCells(parent)) {
			mxCellState state = jgraph.getView().getState(cell);
			state.setStyle(jgraph.getCellStyle(cell));
			jgraph.updateCellSize(cell);
		}
		jgraph.getModel().endUpdate();
		jgraph.refresh();
	}
	
	private void init_graph_settings() {
		// set default values
		spacingBottom = 0;
		spacingTop = 0;
		spacingLeft = 0;
		spacingRight = 0;
		fontColor = "black";
		fillColor = "white";
		vertexShape = mxConstants.SHAPE_RECTANGLE;
		graphElemetsFont = "Monospaced";
		vertexTextAlign = mxConstants.ALIGN_LEFT;
		
		// initialize style sheet constant
		styleSheet = new mxStylesheet();
		
		// initialize style sheet parameters
		String tmp = MainManager.config.getProperty(IGraphSettings.DEF_STYLE_VERTEX_SPACING_LEFT);
		if (tmp != null) {
			spacingLeft = Integer.valueOf(tmp);
		}
		tmp = MainManager.config.getProperty(IGraphSettings.DEF_STYLE_VERTEX_SPACING_TOP);
		if (tmp != null) {
			spacingTop = Integer.valueOf(tmp);
		}
		tmp = MainManager.config.getProperty(IGraphSettings.DEF_STYLE_VERTEX_SPACING_RIGHT);
		if (tmp != null) {
			spacingRight = Integer.valueOf(tmp);
		}
		tmp = MainManager.config.getProperty(IGraphSettings.DEF_STYLE_VERTEX_SPACING_BOTTOM);
		if (tmp != null) {
			spacingBottom = Integer.valueOf(tmp);
		}
		tmp = MainManager.config.getProperty(IGraphSettings.DEF_STYLE_VERTEX_FONT_COLOR);
		if (tmp != null) {
			fontColor = tmp;
		}
		tmp = MainManager.config.getProperty(IGraphSettings.DEF_STYLE_VERTEX_FILL_COLOR);
		if (tmp != null) {
			fillColor = tmp;
		}
		tmp = MainManager.config.getProperty(IGraphSettings.DEF_STYLE_VERTEX_SHAPE);
		if (tmp != null) {
			vertexShape = tmp;
		}
		tmp = MainManager.config.getProperty(IGraphSettings.DEF_STYLE_VERTEX_TEXT_ALIGN);
		if (tmp != null) {
			vertexTextAlign = tmp;
		}
		tmp = MainManager.config.getProperty(IGraphSettings.DEF_STYLE_GRAPH_ELEMENTS_FONT);
		if (tmp != null) {
			graphElemetsFont = tmp;
		}
	}
	
	private ArrayList<Object> findPath(final Vertex source, final Vertex target) {
		mxCell from = vertex_cell.get(source);
		mxCell to = vertex_cell.get(target);
		if (from == null || to == null)
			return null;

		ArrayList<Object> paths = new ArrayList<Object>();
		ArrayList<Object> visited = new ArrayList<Object>();
		dfs(from, to, paths, visited, null);
		paths.add(from);
		return paths;
	}
	
	private void dfs(final Object root, final Object target, List<Object> paths, List<Object> visited, Object prevEdge) {
		visited.add(root);
		Object[] e = (graph.isDirected()) ? jgraph.getOutgoingEdges(root) : jgraph.getConnections(root);
		if (e == null)
			return;
		
		for (Object edge : e) {
			if (edge.equals(prevEdge))
				continue;
			Object[] opp = jgraph.getOpposites(new Object[] { edge }, root);

			if (opp != null && opp.length > 0) {
				Object o = opp[0];

				if (o.equals(target)) {
					paths.add(o);
					paths.add(edge);
				} else {
					if (visited.contains(o))
						continue;
					int l = paths.size();
					dfs(o, target, paths, visited, edge);
					if (l != paths.size()) {
						paths.add(edge);
						paths.add(o);
					}
				}
			}
		}
		visited.remove(root);
	}
}
